//
// Created by tcj on 2022/2/23.
//

#include "fiber.h"
#include "config.h"
#include "macro.h"
#include "atomic"
#include "scheduler.h"
#include "log.h"

namespace sylar{

    static Logger::ptr g_logger = SYLAR_LOG_NAME("system");
//  全局静态变量 ，，用于生成协程id
    static std::atomic<uint64_t> s_fiber_id {0};
//    全局静态变量，，用于统计当前协程数
    static std::atomic<uint64_t> s_fiber_count {0};

//   线程局部变量， 当前线程正在运行协程
    static thread_local  Fiber *t_fiber = nullptr;
//    线程局部变量，当前线程的主协程
    static thread_local Fiber::ptr t_threadFiber = nullptr;

    static ConfigVar<uint32_t>::ptr g_fiber_stack_size =
            Config::Lookup<uint32_t>("fiber.stack_size",1024*1024,"fiber stack size");

    class MallocStatckAllocator{
    public:
        static void* Alloc(size_t size){
            return malloc(size);
        }
        static void Dealloc(void* vp,size_t size){
            return free(vp);
        }
    };

    //别名指定,类似于typedef
    using StackAllocator = MallocStatckAllocator;

    uint64_t Fiber::GetFiberId() {
        if(t_fiber){
            return t_fiber->getId();
        }
        return 0;
    }

//   主协程
    Fiber::Fiber() {
        m_state = EXEC;
        SetThis(this);
// getcontext(ucontext_t * ucp);-->获取当前上下文, 初始化ucp结构体, 将当前上下文保存到ucp中
        if(getcontext(&m_ctx)){
            SYLAR_ASSERT2(false,"getcontext");
        }

        ++s_fiber_count;
//        SYLAR_LOG_DEBUG(g_logger) <<"Fiber::Fiber main";
    }

//  创建新的协程
//    需要stack--协程栈大小、，
//    还有cb--执行函数,
//    use-caller:是否在mainFiber上调度
    Fiber::Fiber(std::function<void()> cb, size_t stacksize,bool use_caller)
            :m_id(++s_fiber_id)
            ,m_cb(cb){
        ++s_fiber_count;
        m_stacksize = stacksize?stacksize:g_fiber_stack_size->getValue();

        m_stack = StackAllocator::Alloc(m_stacksize);
        if(getcontext(&m_ctx)){
            SYLAR_ASSERT2(false,"getcontext");
        }
        m_ctx.uc_link = nullptr;    //将后继上下文指向nullptr
        m_ctx.uc_stack.ss_sp = m_stack;  //指定栈空间
        m_ctx.uc_stack.ss_size = m_stacksize;   //指定栈空间大小

// makecontext(ucontext_t *ucp, void(*func)(), int argc, ...);
//  创建一个上下文,执行函数,以及参数
        if(!use_caller){
            makecontext(&m_ctx,&Fiber::MainFunc,0);
        }else{
            makecontext(&m_ctx,&Fiber::CallerMainFunc,0);
        }



//        SYLAR_LOG_DEBUG(g_logger)<<"Fiber::Fiber id ="<<m_id;

    }

    Fiber::~Fiber() {
        --s_fiber_count;
        if(m_stack){
            SYLAR_ASSERT(m_state == TERM || m_state == INIT
                        || m_state == EXCEPT);
            StackAllocator::Dealloc(m_stack,m_stacksize);
        }else{ // 没有栈就是主协程
            SYLAR_ASSERT(!m_cb);
            SYLAR_ASSERT(m_state == EXEC);

            Fiber* cur = t_fiber;
            if(cur == this){
                SetThis(nullptr);
            }
        }
        SYLAR_LOG_DEBUG(g_logger)<<"Fiber::~Fiber id="<<m_id
                    <<" total="<<s_fiber_count;
    }

//    重置后续协程函数，并重置状态---
//      重置协程就是重复利用已经结束的协程，复用其栈空间，创建新协程
    void Fiber::reset(std::function<void()> cb) {
        SYLAR_ASSERT(m_stack);
        SYLAR_ASSERT(m_state ==TERM || m_state==INIT
                    || m_state == EXCEPT);
        m_cb = cb;
        if(getcontext(&m_ctx)){
            SYLAR_ASSERT2(false,"getcontext");
        }

        m_ctx.uc_link = nullptr;
        m_ctx.uc_stack.ss_sp = m_stack;
        m_ctx.uc_stack.ss_size = m_stacksize;

        makecontext(&m_ctx,&Fiber::MainFunc,0);
        m_state = INIT;
    }

//    pre_state:执行为当前线程的主协程
//   将当前线程切换到执行状态
    void Fiber::call() {
        SetThis(this);
        m_state = EXEC;
//        int swapcontext(ucontext_t *oucp, ucontext_t *ucp);
//          保存当前上下文至oucp, 激活ucp上下文
        if(swapcontext(&t_threadFiber->m_ctx,&m_ctx)){
            SYLAR_ASSERT2(false,"swapcontext");
        }
    }

//    将当前线程切换至后台
    void Fiber::back() {
        SetThis(t_threadFiber.get());
        if(swapcontext(&m_ctx,&t_threadFiber->m_ctx)){
            SYLAR_ASSERT2(false,"swapcontext");
        }

    }


//    协程原语的实现，resume,yield---swapin,swapout
//              (一个是切换当前协程到执行状态,一个是当前协程让出执行权)
//    切换到当前协程执行-->post_state:EXEC
    void Fiber::swapIn() {
        SetThis(this);
        SYLAR_ASSERT(m_state != EXEC);
        m_state = EXEC;

        if(swapcontext((&Scheduler::GetMinFiber()->m_ctx),&m_ctx)){
            SYLAR_ASSERT2(false,"swapcontext");
        }
//        SYLAR_LOG_DEBUG(g_logger)<<"swapin::::";

    }
//    是当前协程让出执行权 -->
    void Fiber::swapOut() {
        SetThis(Scheduler::GetMinFiber());
        if(swapcontext(&m_ctx,&Scheduler::GetMinFiber()->m_ctx)){
            SYLAR_ASSERT2(false,"swapcontext");
        }
    }

//    设置当前协程的运行协程
//    f:运行协程
    void Fiber::SetThis(Fiber* f) {
        t_fiber = f;
    }

//    返回当前协程
//    Fiber::ptr Fiber::GetThis() {
//        if(t_fiber){
//            return t_fiber->shared_from_this();
//        }
//        Fiber::ptr main_fiber(new Fiber);
//        SYLAR_ASSERT(t_fiber == main_fiber.get());
//        t_threadFiber = main_fiber;
//        return t_fiber->shared_from_this();
//    }
    // 创建一个主协程
    Fiber::ptr Fiber::GetThis() {
        if(t_fiber) {
            return t_fiber->shared_from_this();
        }
        Fiber::ptr main_fiber(new Fiber);
        SYLAR_ASSERT(t_fiber == main_fiber.get());
        t_threadFiber = main_fiber;
        return t_fiber->shared_from_this();
    }

    void Fiber::YieldToReady() {
        Fiber::ptr cur = GetThis();
        SYLAR_ASSERT(cur->m_state==EXEC);
        cur->m_state = READY;
        cur->swapOut();
    }

    void Fiber::YieldToHold() {
        Fiber::ptr cur = GetThis();
        SYLAR_ASSERT(cur->m_state == EXEC);
//        cur->m_state = HOLD;
        cur->swapOut();
    }

    uint64_t Fiber::TotalFiber() {
        return s_fiber_count;
    }

//   一般协程的回调主函数
//  协程入口函数：
    void Fiber::MainFunc() {
        Fiber::ptr cur = GetThis();
        SYLAR_ASSERT(cur);
        try{
            cur->m_cb();
            cur->m_cb = nullptr;
            cur->m_state = TERM;
        }catch(std::exception& ex){
            cur->m_state = EXCEPT;
            SYLAR_LOG_ERROR(g_logger)<<"Fiber Except: "<<ex.what()
                <<" fiber_id="<<cur->getId()
                <<std::endl
                <<sylar::BacktraceTostring();

        }catch (...){
            cur->m_state = EXCEPT;
            SYLAR_LOG_ERROR(g_logger)<<"Fiber Except"
                        <<" fiber_id="<<cur->getId()
                        <<std::endl
                        <<sylar::BacktraceTostring();
        }
// 如果没有这段会产生内存泄漏
        auto raw_ptr = cur.get();
        cur.reset();
        raw_ptr->swapOut();

        SYLAR_ASSERT2(false,"never reach fiber_id="<<std::to_string(raw_ptr->getId()));
    }

//   持有调度器协程的回调主函数
    void Fiber::CallerMainFunc() {
        Fiber::ptr cur = GetThis();
        SYLAR_ASSERT(cur);
        try{
            cur->m_cb();
            cur->m_cb = nullptr;
            cur->m_state = TERM;
        }catch(std::exception& ex){
            cur->m_state = EXCEPT;
            SYLAR_LOG_ERROR(g_logger)<<"Fiber Except: "<<ex.what()
                                     <<" fiber_id="<<cur->getId()
                                     <<std::endl
                                     <<sylar::BacktraceTostring();

        }catch (...){
            cur->m_state = EXCEPT;
            SYLAR_LOG_ERROR(g_logger)<<"Fiber Except"
                                     <<" fiber_id="<<cur->getId()
                                     <<std::endl
                                     <<sylar::BacktraceTostring();
        }

        auto raw_ptr = cur.get();
        cur.reset();
        raw_ptr->back();

        SYLAR_ASSERT2(false,"never reach fiber_id="<<std::to_string(raw_ptr->getId()));

    }


}
